﻿using System.Linq.Expressions;
using RpcHelper;
using RpcModel;
using SqlSugar;
using SqlSugarHelper.Repository;

namespace SqlSugarHelper.Respositry
{
    public class BasicRepository<T> : IRepository<T> where T : class, new()
    {
        private ISqlClientFactory _Factory;
        private string _ConfigId;
        protected BasicRepository(ISqlClientFactory factory, string configId)
        {
            _Factory = factory;
            _ConfigId = configId;
        }
        private SqlSugarProvider _Provider;

        public SqlSugarProvider Provider
        {
            get
            {
                if (_Provider == null)
                {
                    _Provider = this._Factory.GetProvider(_ConfigId);
                }
                return _Provider;
            }
        }
        public ISqlQueue<T> BeginQueue()
        {
            return new LocalQueue<T>(this.Provider);
        }
        public ISugarQueryable<T> Queryable
        {
            get => this._Factory.GetQueryable<T>(_ConfigId);
        }

        public void Insert(T item)
        {
            this.Provider.Insertable<T>(item).ExecuteCommand();
        }

        public long InsertReutrnIdentity(T item)
        {
            return this.Provider.Insertable<T>(item).ExecuteReturnBigIdentity();
        }

        public void Insert(T[] items)
        {
            IInsertable<T> insert = this.Provider.Insertable<T>(items);
            if (items.Length < 500)
            {
                insert.UseParameter().ExecuteCommand();
                return;
            }
            insert.ExecuteCommand();
        }


        public int Count()
        {
            return this.Queryable.Count();
        }

        public int Count(Expression<Func<T, bool>> filter)
        {
            return this.Queryable.Where(filter).Count();
        }

        public bool Delete(Expression<Func<T, bool>> filter)
        {
            return this.Provider.Deleteable<T>().Where(filter).ExecuteCommandHasChange();
        }

        public IDeleteable<T> Delete()
        {
            return this.Provider.Deleteable<T>();
        }
        public bool Delete<Other>(Expression<Func<T, Other, bool>> filter) where Other : class, new()
        {
            return this.Provider.Deleteable<T>().Where(p => SqlFunc.Subqueryable<Other>().Where(filter).Any()).ExecuteCommandHasChange();
        }


        public int ExecuteSql(string sql, params SugarParameter[] param)
        {
            return this.Provider.Ado.ExecuteCommand(sql, param);
        }

        public T Get(string sql, params SugarParameter[] param)
        {
            return this.Provider.Ado.SqlQuerySingle<T>(sql, param);
        }

        public T Get(Expression<Func<T, bool>> filter)
        {
            return this.Queryable.Where(filter).First();
        }

        public T Get(Expression<Func<T, bool>> filter, string orderby)
        {
            return this.Queryable.Where(filter).OrderBy(orderby).First();
        }

        public Result Get<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Where(filter).Select(selector).First();
        }

        public Result Get<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, Result>> selector, string orderby)
        {
            return this.Queryable.Where(filter).OrderBy(orderby).Select(selector).First();
        }
        public Result Get<Result>(ISugarQueryable<Result>[] list, string orderBy) where Result : class, new()
        {
            return this.Provider.UnionAll<Result>(list).OrderBy(orderBy).First();
        }
        public Result[] Gets<Result>(ISugarQueryable<Result>[] list, string orderBy) where Result : class, new()
        {
            return this.Provider.UnionAll<Result>(list).OrderBy(orderBy).ToArray();
        }
        public Result[] Gets<Table, Result>(ISugarQueryable<Table>[] list, string selector, string orderBy) where Table : class, new()
        {
            return this.Provider.UnionAll<Table>(list).Select<Result>(selector).OrderBy(orderBy).ToArray();
        }
        public Result Get<Table, Result>(ISugarQueryable<Table>[] list, string selector, string orderBy) where Table : class, new()
        {
            return this.Provider.UnionAll<Table>(list).Select<Result>(selector).OrderBy(orderBy).First();
        }
        public Result[] Gets<Table, Result>(ISugarQueryable<Table>[] list, string selector) where Table : class, new()
        {
            return this.Provider.UnionAll<Table>(list).Select<Result>(selector).ToArray();
        }
        public Result[] Gets<Table, Result>(ISugarQueryable<Table>[] list, Expression<Func<Table, Result>> selector, string orderBy) where Table : class, new()
        {
            return this.Provider.UnionAll<Table>(list).Select<Result>(selector).OrderBy(orderBy).ToArray();
        }
        public Result Get<Table, Result>(ISugarQueryable<Table>[] list, Expression<Func<Table, Result>> selector, string orderBy) where Table : class, new()
        {
            return this.Provider.UnionAll<Table>(list).Select(selector).OrderBy(orderBy).First();
        }

        public Result[] Gets<Table, Result>(ISugarQueryable<Table>[] list, Expression<Func<Table, Result>> selector) where Table : class, new()
        {
            return this.Provider.UnionAll<Table>(list).Select(selector).ToArray();
        }
        public Result[] Gets<Result>(ISugarQueryable<Result>[] list) where Result : class, new()
        {
            return this.Provider.UnionAll<Result>(list).ToArray();
        }

        public T[] GetAll()
        {
            return this.Queryable.ToArray();
        }

        public T[] GetAll(string orderby)
        {
            return this.Queryable.OrderBy(orderby).ToArray();
        }

        public Result[] GetAll<Result>(Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Select(selector).ToArray();
        }

        public Result[] GetAll<Result>(Expression<Func<T, Result>> selector, string orderby)
        {
            return this.Queryable.Select(selector).OrderBy(orderby).ToArray();
        }


        public Result[] Gets<Result>(string sql, params SugarParameter[] param) where Result : class, new()
        {
            return this.Provider.Ado.SqlQuery<Result>(sql, param).ToArray();
        }

        public T[] Gets(Expression<Func<T, bool>> filter)
        {
            return this.Queryable.Where(filter).ToArray();
        }

        public T[] Gets(Expression<Func<T, bool>> filter, string orderby)
        {
            return this.Queryable.Where(filter).OrderBy(orderby).ToArray();
        }

        public Result[] Gets<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Where(filter).Select(selector).ToArray();
        }

        public Result[] Gets<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, Result>> selector, string orderby)
        {
            return this.Queryable.Where(filter).OrderBy(orderby).Select(selector).ToArray();
        }

        public Result[] GroupBy<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, object>> group, Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Where(filter).GroupBy(group).Select(selector).ToArray();
        }
        public Result[] GroupBy<Result>(Expression<Func<T, object>> group, Expression<Func<T, Result>> selector)
        {
            return this.Queryable.GroupBy(group).Select(selector).ToArray();
        }
        public Result[] GroupBy<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, object>> group, Expression<Func<T, bool>> having, Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Where(filter).GroupBy(group).Having(having).Select(selector).ToArray();
        }

        public bool IsExist<TResult>(Expression<Func<T, bool>> filter, Expression<Func<T, TResult>> selector)
        {
            TResult result = this.Queryable.Where(filter).Select(selector).First();
            return result != null && !result.Equals(default(TResult));
        }

        public Result[] Join<TInner, Result>(Expression<Func<T, TInner, bool>> joinExpression,
            Expression<Func<T, TInner, bool>> filter,
            Expression<Func<T, TInner, Result>> selector) where TInner : class
        {
            return this.Queryable.InnerJoin<TInner>(joinExpression).Where(filter).Select(selector).ToArray();
        }
        public Result[] JoinQuery<TInner, Result>(Expression<Func<T, TInner, bool>> joinExpression,
          Expression<Func<T, TInner, bool>> filter,
          Expression<Func<T, TInner, Result>> selector,
          IBasicPage pager,
          out long count) where TInner : class
        {
            ISugarQueryable<T, TInner> table = this.Queryable.InnerJoin<TInner>(joinExpression).Where(filter);
            count = table.Clone().Count();
            return table.Clone().OrderBy(pager.OrderBy).Select(selector).Skip((pager.Index - 1) * pager.Size).Take(pager.Size).ToArray();
        }
        public Result[] JoinQuery<TInner, Result>(Expression<Func<T, TInner, bool>> joinExpression,
          Expression<Func<T, TInner, bool>> filter,
          Expression<Func<T, TInner, Result>> selector,
          int index,
          int size,
           Expression<Func<T, TInner, object>> orderby,
           OrderByType byType,
          out long count) where TInner : class
        {
            ISugarQueryable<T, TInner> table = this.Queryable.InnerJoin<TInner>(joinExpression).Where(filter);
            count = table.Clone().Count();
            return table.Clone().OrderBy(orderby, byType).Select(selector).Skip((index - 1) * size).Take(size).ToArray();
        }

        public Result[] JoinGroupBy<TInner, TKey, TResult, Result, GroupKey>(Expression<Func<T, TInner, bool>> joinExpression,
            Expression<Func<T, TInner, bool>> filter,
            Expression<Func<T, TInner, object>> group,
            Expression<Func<T, TInner, Result>> selector) where TInner : class
        {
            return this.Queryable.InnerJoin<TInner>(joinExpression).Where(filter).GroupBy(group).Select(selector).ToArray();
        }

        public Result Max<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Where(filter).Max(selector);
        }

        public Result Min<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Where(filter).Min(selector);
        }
        public Result Avg<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Where(filter).Avg(selector);
        }
        public Result Max<Result>(Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Max(selector);
        }
        public Result Min<Result>(Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Min(selector);
        }

        public Result Avg<Result>(Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Avg(selector);
        }
        public T[] Query(Expression<Func<T, bool>> filter, IBasicPage pager, out int count)
        {
            ISugarQueryable<T> table = this.Queryable.Where(filter);
            count = table.Clone().Count();
            return table.Clone().OrderBy(pager.OrderBy).Skip((pager.Index - 1) * pager.Size).Take(pager.Size).ToArray();
        }

        public T[] Query(IBasicPage pager, out long count)
        {
            ISugarQueryable<T> table = this.Queryable;
            count = table.Clone().Count();
            return table.Clone().OrderBy(pager.OrderBy).Skip((pager.Index - 1) * pager.Size).Take(pager.Size).ToArray();
        }

        public Result[] Query<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, Result>> selector, IBasicPage pager, out long count)
        {
            ISugarQueryable<T> table = this.Queryable.Where(filter);
            count = table.Clone().Count();
            return table.Clone().OrderBy(pager.OrderBy).Select(selector).Skip((pager.Index - 1) * pager.Size).Take(pager.Size).ToArray();
        }

        public bool Update(T[] datas, params string[] ignoreColumn)
        {
            if (ignoreColumn.IsNull())
            {
                return Provider.Updateable(datas).ExecuteCommandHasChange();
            }
            return Provider.Updateable(datas).IgnoreColumns(ignoreColumn).ExecuteCommandHasChange();
        }
        public bool UpdateOnly(T[] datas, Expression<Func<T, object>> setCol)
        {
            return Provider.Updateable(datas).UpdateColumns(setCol).ExecuteCommandHasChange();
        }
        public bool Update(T[] datas, Expression<Func<T, object>> filter)
        {
            return Provider.Updateable(datas).WhereColumns(filter).ExecuteCommandHasChange();
        }
        public bool Update<Set>(Set set, Expression<Func<T, bool>> filter)
        {
            return Provider.Updateable<T>(set).Where(filter).ExecuteCommandHasChange();
        }
        public bool Update<Set>(Set set, Expression<Func<T, object>> filter)
        {
            return Provider.Updateable<T>(set).WhereColumns(filter).ExecuteCommandHasChange();
        }

        public bool Update(T data, string[] column)
        {
            return Provider.Updateable(data).UpdateColumns(column).ExecuteCommandHasChange();
        }
        public bool Update(T data, string[] column, Expression<Func<T, bool>> filter)
        {
            return Provider.Updateable(data).UpdateColumns(column).Where(filter).ExecuteCommandHasChange();
        }
        public bool Update(Expression<Func<T, T>> columns, Expression<Func<T, bool>> filter)
        {
            return Provider.Updateable<T>().SetColumns(columns).Where(filter).ExecuteCommandHasChange();
        }
        public bool Update(Expression<Func<T, bool>> columns, Expression<Func<T, bool>> filter)
        {
            return Provider.Updateable<T>().SetColumns(columns).Where(filter).ExecuteCommandHasChange();
        }
        public int UpdateByRowIndex(Expression<Func<T, T>> columns, Expression<Func<T, bool>> filter)
        {
            return Provider.Updateable<T>().SetColumns(columns).Where(filter).ExecuteCommand();
        }
        public bool Update(Expression<Func<T, T>> columns, string[] whereColumn)
        {
            return Provider.Updateable<T>().SetColumns(columns).WhereColumns(whereColumn).ExecuteCommandHasChange();
        }
        public bool Update(Expression<Func<T, T>> columns, Expression<Func<T, object>> filter)
        {
            return Provider.Updateable<T>().SetColumns(columns).WhereColumns(filter).ExecuteCommandHasChange();
        }

        public bool Update(Expression<Func<T, T>> columns)
        {
            return Provider.Updateable<T>().SetColumns(columns).ExecuteCommandHasChange();
        }
        public IUpdateable<T> Update()
        {
            return Provider.Updateable<T>();
        }
        public bool Update<Set>(T source, Set set)
        {
            string[] cols = source.Merge(set);
            if (cols.Length == 0)
            {
                return false;
            }
            return this.Update(source, cols);
        }

        public bool Update(T source, T set)
        {
            string[] cols = source.Merge<T>(set);
            if (cols.Length == 0)
            {
                return false;
            }
            return Update(source, cols);
        }

        public Result Sum<Result>(Expression<Func<T, bool>> filter, Expression<Func<T, Result>> selector)
        {
            return this.Queryable.Where(filter).Sum(selector);
        }

        public bool AddOrUpdate(List<T> datas)
        {
            return this.Provider.Storageable<T>(datas).ExecuteCommand() > 0;
        }
        public IStorageable<T> Storageable(List<T> datas)
        {
            return this.Provider.Storageable<T>(datas);
        }
        public bool AddOrUpdate(List<T> datas, Func<StorageableInfo<T>, bool> insert, Func<StorageableInfo<T>, bool> update)
        {
            return this.Provider.Storageable<T>(datas).SplitInsert(insert).SplitUpdate(update).ExecuteCommand() > 0;
        }
        public int Insert(List<T> datas, Expression<Func<T, object>> filter)
        {
            StorageableResult<T> table = this.Provider.Storageable<T>(datas).WhereColumns(filter).ToStorage();
            return table.AsInsertable.ExecuteCommand();
        }
        public bool AddOrUpdate(List<T> datas, Expression<Func<T, object>> filter)
        {
            StorageableResult<T> table = this.Provider.Storageable<T>(datas).WhereColumns(filter).ToStorage();
            int row = table.AsInsertable.ExecuteCommand();
            row += table.AsUpdateable.ExecuteCommand();
            return row > 0;
        }
        public bool AddOrUpdate(List<T> datas, Expression<Func<T, object>> filter, Expression<Func<T, object>> update)
        {
            StorageableResult<T> table = this.Provider.Storageable<T>(datas).WhereColumns(filter).ToStorage();
            int row = table.AsInsertable.ExecuteCommand();
            row += table.AsUpdateable.UpdateColumns(update).ExecuteCommand();
            return row > 0;
        }

    }
}
